package com.hmall.gateway.route;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.hmall.common.utils.CollUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * 动态路由加载器
 * 负责从Nacos配置中心加载网关路由配置，并实现动态更新
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    /**
     * 路由定义写入器，用于更新网关路由
     */
    private final RouteDefinitionWriter writer;

    /**
     * Nacos配置管理器，用于获取配置
     */
    private final NacosConfigManager nacosConfigManager;

    // Nacos配置相关常量
    private final String dataId = "gateway-routes.json"; // 配置数据ID
    private final String group = "DEFAULT_GROUP";       // 配置分组

    // 存储已加载的路由ID，用于后续删除旧路由
    private final Set<String> routeIds = new HashSet<>();

    /**
     * 初始化方法
     * 在Bean初始化时注册Nacos配置监听器并首次加载路由配置
     */
    @PostConstruct
    public void initRouteConfigListener() throws NacosException {
        log.info("开始初始化动态路由加载器");

        // 1. 从Nacos获取配置并注册监听器
        // 参数说明：dataId-配置ID，group-配置分组，timeout-超时时间，listener-配置变更监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        // 配置变更通知的执行器，这里使用默认执行器
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        // 当Nacos配置变更时触发此方法
                        updateConfigInfo(configInfo);
                    }
                });

        // 2. 首次启动时主动更新一次配置（确保初始配置加载）
        updateConfigInfo(configInfo);

        log.info("动态路由加载器初始化完成");
    }

    /**
     * 更新路由配置信息
     * @param configInfo Nacos推送的配置内容（JSON格式的路由定义列表）
     */
    private void updateConfigInfo(String configInfo) {
        log.debug("监听到路由配置变更，配置内容: {}", configInfo);

        try {
            // 1. 将JSON配置反序列化为路由定义列表
            List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);

            // 2. 清除旧路由配置
            for (String routeId : routeIds) {
                // 异步删除旧路由
                writer.delete(Mono.just(routeId)).subscribe();
            }
            // 清空已记录的路由ID
            routeIds.clear();

            // 3. 检查是否有新路由配置
            if (CollUtils.isEmpty(routeDefinitions)) {
                log.warn("接收到空路由配置，跳过更新");
                return;
            }

            // 4. 加载新路由配置
            for (RouteDefinition routeDefinition : routeDefinitions) {
                // 4.1 保存新路由定义
                writer.save(Mono.just(routeDefinition)).subscribe();
                // 4.2 记录路由ID，用于下次更新时删除
                routeIds.add(routeDefinition.getId());
                log.info("已加载路由: {}", routeDefinition.getId());
            }

            log.info("成功更新路由配置，共加载 {} 条路由", routeDefinitions.size());
        } catch (Exception e) {
            log.error("更新路由配置失败", e);
        }
    }
}